package minov.parser;

import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;

/**
 * Argument for parsing command arguments.
 * Command is composed from command name and arguments.
 * So at parsing will be checked, if input argument is command alias.
 * If it is so, then all next input arguments will be stored as arguments
 * of command, that this class represents.
 * 
 * Source:
 * 
 * <pre>
 * checkoutCommand = new CommandArgument(&quot;checkout&quot;, &quot;co&quot;);
 * parser.addArgument(&quot;checkoutCommand&quot;, checkoutCommand);
 * </pre>
 * 
 * Accepts input (where svn is application):
 * 
 * <pre>
 * checkout http://www.code.google/myproject username
 * </pre>
 * 
 * Result of parsing is, that argument returns
 * <code> http://www.code.google/myproject</code> and <code>username</code> as
 * result by method {@link getArguments}
 * 
 * <h3>Example</h3>
 * 
 * \include CommandExample.java
 * prints
 * \include CommandExampleResult.txt
 * 
 * @author Jakub Michalko
 */
public class CommandArgument extends ArgumentObject {

   /**
    * Constructor
    * 
    * @param shortName
    *           One letter abbreviation of the argument.
    * @param fullName
    *           Long name of the argument.
    * @param validValues
    *           List of the enum type values.
    * @param defaultValues
    *           The default values to be used.
    */
   @SuppressWarnings("unchecked")
   public CommandArgument(String... commandAliases) {
      super();
      super.setValueCountRange(NO_ARGUMENT, UNLIMITED_ARGUMENT);
      for (String possibleCommand : commandAliases) {
         _commandAliases.add(possibleCommand);
      }
   }

   /**
    * Result entered values.
    * If
    */
   private ArrayList<String> _commandArguments = new ArrayList<String>();

   private String _commandName = null;

   private Set<String> _commandAliases = new HashSet<String>();

   private boolean argumentIsCommand(String argument) {
      return _commandAliases.contains(argument);
   }

   /**
    * Method for processing the parameters. Used by the parser.
    * 
    * @param arguments
    *           Deque with the arguments
    */
   @Override
   public void parseArguments(Deque<ParsedArgumentData> arguments) {
      if (arguments.isEmpty())
         return;

      ParsedArgumentData inputArgument = arguments.peek();
      if (argumentIsCommand(inputArgument.value) == false) {
         return;
      }
      // remove argument from queue
      arguments.poll();
      _commandName = inputArgument.value;

      // add all input arguments as arguments of
      // current command
      while (arguments.isEmpty() == false) {
         ParsedArgumentData commandArgument = arguments.poll();
         _commandArguments.add(commandArgument.originalText);
      }
      // parsed count = command name + all arguments
      Integer parsedArguments = 1 + _commandArguments.size();
      this.setEnteredValues(parsedArguments);

      return;
   }

   /*
    * format string for validation result
    */

   private static final String UNDEFINED_DEFAULTS_FORMAT_STRING =
         "Argument %s has not defined default values.";

   private static final String BAD_NUMBER_FORMAT_STRING =
         "Argument %s is not within specified boundaries.";

   /**
    * Method for validation of the parsed arguments.
    * 
    * @return ValidationResult object with the result
    */
   @Override
   public final ValidationResult validateArgument() {

      return super.validateArgument();

   }

   /**
    * Return command name
    */
   public String getValue() {
      return _commandName;
   }

   public String[] getArguments() {
      return _commandArguments.toArray(new String[0]);
   }

   @Override
   public void reset() {
      super.reset();
      _commandArguments.clear();
      _commandName = null;
   }

}
